home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AppleScript - The Beta Release
/
AppleScript - The Beta Release.iso
/
Documentation
/
develop
/
Better Apple Event Coding
/
Code Samples
/
TEDocument.cp
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Text File
|
1992-10-16
|
38.9 KB
|
1,559 lines
|
[
TEXT/MPS
]
/*------------------------------------------------------------------------------------------
Program: CPlusTESample 2.0AE
File: TEDocument.cp
Uses: TEDocument.h
TESample.h
by Andrew Shebanow
of Apple Macintosh Developer Technical Support
with modifications by Eric Berdahl
Copyright © 1989-1990 Apple Computer, Inc.
Copyright © 1992 Eric Berdahl
All rights reserved.
------------------------------------------------------------------------------------------*/
// Mac Includes
#ifndef __TYPES__
#include <Types.h>
#endif
#ifndef __QUICKDRAW__
#include <QuickDraw.h>
#endif
#ifndef __FONTS__
#include <Fonts.h>
#endif
#ifndef __EVENTS__
#include <Events.h>
#endif
#ifndef __CONTROLS__
#include <Controls.h>
#endif
#ifndef __WINDOWS__
#include <Windows.h>
#endif
#ifndef __MENUS__
#include <Menus.h>
#endif
#ifndef __TEXTEDIT__
#include <TextEdit.h>
#endif
#ifndef __DIALOGS__
#include <Dialogs.h>
#endif
#ifndef __DESK__
#include <Desk.h>
#endif
#ifndef __SCRAP__
#include <Scrap.h>
#endif
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
#ifndef __MEMORY__
#include <Memory.h>
#endif
#ifndef __SEGLOAD__
#include <SegLoad.h>
#endif
#ifndef __FILES__
#include <Files.h>
#endif
#ifndef __OSUTILS__
#include <OSUtils.h>
#endif
#ifndef __TRAPS__
#include <Traps.h>
#endif
#ifndef __PACKAGES__
#include <Packages.h>
#endif
#ifndef __ERRORS__
#include <Errors.h>
#endif
#ifndef __AEOBJECTS__
#include "AEObjects.h"
#endif
#ifndef __AEREGISTRY__
#include "AERegistry.h"
#endif
#ifndef __TEDOCUMENT__
#include "TEDocument.h"
#endif
#ifndef __TESAMPLE__
#include "TESample.h"
#endif
extern "C" {
// prototypes for functions that can't belong to TEDocument, but
// which are closely tied into our documents
pascal ClikLoopProcPtr GetOldClikLoop();
pascal void PascalClikLoop();
void CommonAction(ControlHandle control,short* amount);
pascal void VActionProc(ControlHandle control,short part);
pascal void HActionProc(ControlHandle control,short part);
// this routine is written in Assembler, since it needs to tweak registers
pascal void ASMCLIKLOOP();
};
// kTextMargin is the number of pixels we leave blank at the edge of the window.
const short kTextMargin = 2;
// kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
// destination rectangle so that word wrap and horizontal scrolling can be
// demonstrated.
const short kMaxDocWidth = 576;
// kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
// is called.
const short kMinDocDim = 64;
// kMaxTELength is an arbitrary number used to limit the length of text in the TERec
// so that various errors won't occur from too many characters being in the text.
const short kMaxTELength = 32000;
// kControlInvisible is used the same way to 'turn on' the control.
const short kControlVisible = 0xFF;
// ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
// values for control positioning and sizing.
const short kScrollbarAdjust = 15;
const short kGrowboxAdjust = 15;
const short kScrollbarWidth = 16;
// kTESlop provides some extra security when pre-flighting edit commands.
const short kTESlop = 1024;
// kScrollTweek compensates for off-by-one requirements of the scrollbars
// to have borders coincide with the growbox.
const short kScrollTweek = 2;
// kCrChar is used to match with a carriage return when calculating the
// number of lines in the TextEdit record. kDelChar is used to check for
// delete in keyDowns.
const short kCrChar = 13;
const short kDelChar = 8;
//--------------------------------------------------------------------------------
// TWindowName
//--------------------------------------------------------------------------------
class TWindowName : public MAppleObject
{
public:
TWindowName(TEDocument* itsDocument);
virtual void DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon);
virtual DescType GetAppleClass() const;
virtual void DoAppleGetData(const AppleEvent& message, AppleEvent& reply);
virtual void DoAppleSetData(const AppleEvent& message, AppleEvent& reply);
private:
TWindowName();
TEDocument* fDocument;
};
TWindowName::TWindowName(TEDocument* itsDocument)
{
fDocument = itsDocument;
}
void TWindowName::DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon)
{
switch (refCon)
{
case cSetData:
this->DoAppleSetData(message, reply);
break;
case cGetData:
this->DoAppleGetData(message, reply);
break;
default:
MAppleObject::DoAppleEvent(message, reply, refCon);
break;
}
}
DescType TWindowName::GetAppleClass() const
{
return typeText;
}
void TWindowName::DoAppleGetData(const AppleEvent& message, AppleEvent& reply)
{
MAppleObject::GotRequiredParameters(message);
// Get the name of the window
Str255 name;
GetWTitle(fDocument->GetDocWindow(), name);
// Pack the text into a descriptor
AEDesc nameDesc;
FailOSErr(AECreateDesc(typeText, (Ptr) &name[1], name[0], &nameDesc));
// package the reply
OSErr theErr = AEPutParamDesc(&reply, keyDirectObject, &nameDesc);
// dispose of the descriptor we created and check the reply from
// packaging the reply
OSErr ignoreErr = AEDisposeDesc(&nameDesc);
FailOSErr(theErr);
}
void TWindowName::DoAppleSetData(const AppleEvent& message, AppleEvent& /* reply */)
{
DescType returnedType;
Str255 theName;
Size actualSize;
FailOSErr(AEGetParamPtr(&message, keyAETheData, typeText, &returnedType,
(Ptr) &theName[1], 255, &actualSize));
if (actualSize > 255)
actualSize = 255;
theName[0] = (unsigned char) actualSize;
MAppleObject::GotRequiredParameters(message);
SetWTitle(fDocument->GetDocWindow(), theName);
}
//--------------------------------------------------------------------------------
// TWindowPosition
//--------------------------------------------------------------------------------
class TWindowPosition : public MAppleObject
{
public:
TWindowPosition(TEDocument* itsDocument);
virtual void DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon);
virtual DescType GetAppleClass() const;
virtual void DoAppleGetData(const AppleEvent& message, AppleEvent& reply);
virtual void DoAppleSetData(const AppleEvent& message, AppleEvent& reply);
private:
TWindowPosition();
TEDocument* fDocument;
};
TWindowPosition::TWindowPosition(TEDocument* itsDocument)
{
fDocument = itsDocument;
}
void TWindowPosition::DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon)
{
switch (refCon)
{
case cSetData:
this->DoAppleSetData(message, reply);
break;
case cGetData:
this->DoAppleGetData(message, reply);
break;
default:
MAppleObject::DoAppleEvent(message, reply, refCon);
break;
}
}
DescType TWindowPosition::GetAppleClass() const
{
return cQDPoint;
}
void TWindowPosition::DoAppleGetData(const AppleEvent& message, AppleEvent& reply)
{
MAppleObject::GotRequiredParameters(message);
// Get the position of the window (topLeft of the bounds)
Rect myBounds = (*WindowPeek(fDocument->GetDocWindow())->strucRgn)->rgnBBox;
Point myPosition;
myPosition.v = myBounds.top;
myPosition.h = myBounds.left;
// Pack the position into a descriptor
AEDesc posDesc;
FailOSErr(AECreateDesc(typeQDPoint, (Ptr) &myPosition, sizeof(myPosition), &posDesc));
// package the reply
OSErr theErr = AEPutParamDesc(&reply, keyDirectObject, &posDesc);
// dispose of the descriptor we created and check the reply from
// packaging the reply
OSErr ignoreErr = AEDisposeDesc(&posDesc);
FailOSErr(theErr);
}
void TWindowPosition::DoAppleSetData(const AppleEvent& message, AppleEvent& /* reply */)
{
// Get the new position
DescType returnedType;
Point thePos;
Size actualSize;
FailOSErr(AEGetParamPtr(&message, keyAETheData, typeQDPoint, &returnedType,
(Ptr) &thePos, sizeof(thePos), &actualSize));
MAppleObject::GotRequiredParameters(message);
// the point is for the structure region, and is in global coordinates
// MoveWindow applies to the content region, so we have to massage a little
// the massage is specific to the type of window we are using
thePos.v += 19;
thePos.h++;
// myPoint is now adjusted for the content region
MoveWindow(fDocument->GetDocWindow(), thePos.h, thePos.v, false);
}
//--------------------------------------------------------------------------------
// TWindowBounds
//--------------------------------------------------------------------------------
class TWindowBounds : public MAppleObject
{
public:
TWindowBounds(TEDocument* itsDocument);
virtual void DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon);
virtual DescType GetAppleClass() const;
virtual void DoAppleGetData(const AppleEvent& message, AppleEvent& reply);
virtual void DoAppleSetData(const AppleEvent& message, AppleEvent& reply);
private:
TWindowBounds();
TEDocument* fDocument;
};
TWindowBounds::TWindowBounds(TEDocument* itsDocument)
{
fDocument = itsDocument;
}
void TWindowBounds::DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon)
{
switch (refCon)
{
case cSetData:
this->DoAppleSetData(message, reply);
break;
case cGetData:
this->DoAppleGetData(message, reply);
break;
default:
MAppleObject::DoAppleEvent(message, reply, refCon);
break;
}
}
DescType TWindowBounds::GetAppleClass() const
{
return cQDRectangle;
}
void TWindowBounds::DoAppleGetData(const AppleEvent& message, AppleEvent& reply)
{
MAppleObject::GotRequiredParameters(message);
// Get the bounds of the window
Rect myBounds = (*WindowPeek(fDocument->GetDocWindow())->strucRgn)->rgnBBox;
// Pack the bounds into a descriptor
AEDesc boundsDesc;
FailOSErr(AECreateDesc(typeQDRectangle, (Ptr) &myBounds, sizeof(myBounds), &boundsDesc));
// package the reply
OSErr theErr = AEPutParamDesc(&reply, keyDirectObject, &boundsDesc);
// dispose of the descriptor we created and check the reply from
// packaging the reply
OSErr ignoreErr = AEDisposeDesc(&boundsDesc);
FailOSErr(theErr);
}
void TWindowBounds::DoAppleSetData(const AppleEvent& message, AppleEvent& /* reply */)
{
// Get the new bounds
DescType returnedType;
Rect theBounds;
Size actualSize;
FailOSErr(AEGetParamPtr(&message, keyAETheData, typeQDRectangle, &returnedType,
(Ptr) &theBounds, sizeof(theBounds), &actualSize));
MAppleObject::GotRequiredParameters(message);
// the rectangle is for the structure region, and is in global coordinates
// MoveWindow and SizeWindow apply to the content region, so we have to massage a little
// the massage is specific to the type of window we are using
theBounds.top += 19;
theBounds.left++;
theBounds.bottom -= 2;
theBounds.right -= 2;
// theBounds is now adjusted for the content region
MoveWindow(fDocument->GetDocWindow(), theBounds.left, theBounds.top, false);
SizeWindow(fDocument->GetDocWindow(), theBounds.right - theBounds.left,
theBounds.bottom - theBounds.top, true);
fDocument->UpdateForNewSelection();
fDocument->AdjustScrollbars(true);
}
//--------------------------------------------------------------------------------
// TEditText
//--------------------------------------------------------------------------------
class TEditText : public MAppleObject
{
public:
TEditText(TEDocument* itsDocument, TEHandle itsTE);
virtual void DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon);
virtual DescType GetAppleClass() const;
virtual void DoAppleGetData(const AppleEvent& message, AppleEvent& reply);
virtual void DoAppleSetData(const AppleEvent& message, AppleEvent& reply);
private:
TEditText();
TEDocument* fDocument;
TEHandle fTEHandle;
};
TEditText::TEditText(TEDocument* itsDocument, TEHandle itsTE)
{
fDocument = itsDocument;
fTEHandle = itsTE;
}
void TEditText::DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon)
{
switch (refCon)
{
case cSetData:
this->DoAppleSetData(message, reply);
break;
case cGetData:
this->DoAppleGetData(message, reply);
break;
default:
MAppleObject::DoAppleEvent(message, reply, refCon);
break;
}
}
DescType TEditText::GetAppleClass() const
{
return typeText;
}
void TEditText::DoAppleGetData(const AppleEvent& message, AppleEvent& reply)
{
MAppleObject::GotRequiredParameters(message);
// Pack the text into a descriptor
CharsHandle theText = TEGetText(fTEHandle);
AEDesc textDesc;
HLock((Handle) theText);
OSErr theErr = AECreateDesc(typeText, (Ptr) *theText,
GetHandleSize((Handle) theText), &textDesc);
// Unlock the handle and check the error code
HUnlock((Handle) theText);
FailOSErr(theErr);
// package the reply
theErr = AEPutParamDesc(&reply, keyDirectObject, &textDesc);
// dispose of the descriptor we created and check the reply from
// packaging the reply
OSErr ignoreErr = AEDisposeDesc(&textDesc);
FailOSErr(theErr);
}
void TEditText::DoAppleSetData(const AppleEvent& message, AppleEvent& /* reply */)
{
AEDesc textDesc;
FailOSErr(AEGetParamDesc(&message, keyAETheData, typeText, &textDesc));
MAppleObject::GotRequiredParameters(message);
HLock(textDesc.dataHandle);
TESetText(*textDesc.dataHandle, GetHandleSize(textDesc.dataHandle), fTEHandle);
HUnlock(textDesc.dataHandle);
OSErr ignoreErr = AEDisposeDesc(&textDesc);
HLock((Handle) fTEHandle);
InvalRect(&(*fTEHandle)->viewRect);
HUnlock((Handle) fTEHandle);
fDocument->UpdateForNewSelection();
fDocument->AdjustScrollbars(true);
}
//--------------------------------------------------------------------------------
// TEDocument
//--------------------------------------------------------------------------------
// notice that we pass the resID parameter up to our base class,
// which actually creates the window for us
TEDocument::TEDocument(short resID) : TDocument(resID, kTEFileType)
{
Boolean good;
Rect destRect, viewRect;
good = false;
SetPort(fDocWindow);
GetTERect(&viewRect);
destRect = viewRect;
destRect.right = destRect.left + kMaxDocWidth;
fDocTE = TEStylNew(&destRect, &viewRect);
FailNIL(fDocTE);
// set up TE record
AdjustViewRect();
TEAutoView(true, fDocTE);
fDocClik = (*fDocTE)->clikLoop;
(*fDocTE)->clikLoop = (ClikLoopProcPtr) ASMCLIKLOOP;
fDocVScroll = fDocHScroll = nil;
// get vertical scrollbar
TRY
{
fDocVScroll = GetNewControl(rVScroll, fDocWindow);
FailNILResource(fDocVScroll);
fDocHScroll = GetNewControl(rHScroll, fDocWindow);
FailNILResource(fDocHScroll);
}
RECOVER
{
TEDispose(fDocTE);
if (fDocVScroll)
delete fDocVScroll;
if (fDocHScroll)
delete fDocHScroll;
FailNewMessage(eNoWindow,gFailMessage,kTEDocErrStrings);
}
ENDTRY
TEDocument::UpdateForNewSelection();
AdjustScrollValues(true);
ShowWindow(fDocWindow);
SelectWindow(fDocWindow);
}
TEDocument::~TEDocument()
{
HideWindow(fDocWindow);
if ( fDocTE != nil )
{
TEDispose(fDocTE); // dispose the TEHandle if we got far enough to make one
}
if ( fDocVScroll != nil )
{
DisposeControl(fDocVScroll);
}
if ( fDocHScroll != nil )
{
DisposeControl(fDocHScroll);
}
// base class destructor will dispose of window
}
void TEDocument::DoZoom(short partCode)
{
Rect tRect;
tRect = fDocWindow->portRect;
EraseRect(&tRect);
ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
AdjustScrollbars(true); // adjust, redraw anyway
AdjustTE();
InvalRect(&tRect); // invalidate the whole content
// the scrollbars were taken care of by AdjustScrollbars, so validate ’em
tRect = (*fDocVScroll)->contrlRect;
ValidRect(&tRect);
tRect = (*fDocHScroll)->contrlRect;
ValidRect(&tRect);
}
// Called when a mouseDown occurs in the grow box of an active window.
void TEDocument::DoGrow(EventRecord* theEvent)
{
long growResult;
Rect tRect, tRect2;
tRect = qd.screenBits.bounds;
tRect.left = kMinDocDim;
tRect.top = kMinDocDim;
growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
// see if it really changed size
if ( growResult != 0 )
{
tRect = (*fDocTE)->viewRect;
SizeWindow(fDocWindow, LoWord(growResult), HiWord(growResult), true);
AdjustScrollbars(true);
AdjustTE();
// calculate & validate the region that hasn’t changed so it won’t get redrawn
// Note: we copy rectangles so that we don't take address of object fields.
tRect2 = (*fDocTE)->viewRect;
(void) SectRect(&tRect, &tRect2, &tRect);
tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
ValidRect(&tRect);
tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
}
}
void TEDocument::DoContent(EventRecord* theEvent)
{
Point mouse;
ControlHandle control;
short part, value;
Boolean shiftDown;
Rect teRect;
SetPort(fDocWindow);
mouse = theEvent->where; // get the click position
GlobalToLocal(&mouse);
GetTERect(&teRect);
if ( PtInRect(mouse, &teRect) )
{
// see if we need to extend the selection
shiftDown = (theEvent->modifiers & shiftKey) != 0; // extend if Shift is down
TEClick(mouse, shiftDown, fDocTE);
UpdateForNewSelection();
}
else
{
part = FindControl(mouse, fDocWindow, &control);
switch ( part )
{
case 0:
// do nothing if not in a control
break;
case inThumb:
value = GetCtlValue(control);
part = TrackControl(control, mouse, nil);
if ( part != 0 )
{
value -= GetCtlValue(control);
// value now has CHANGE in value; if value changed, scroll
if ( value != 0 )
if ( control == fDocVScroll )
TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
else TEScroll(value, 0, fDocTE);
}
break;
default: // they clicked in an arrow, so track & scroll
if ( control == fDocVScroll )
value = TrackControl(control, mouse, (ProcPtr) VActionProc);
else value = TrackControl(control, mouse, (ProcPtr) HActionProc);
break;
}
}
}
void TEDocument::DoKeyDown(EventRecord* theEvent)
{
char key;
if (theEvent->modifiers & cmdKey) // don't process command characters
return;
key = (char) (theEvent->message & charCodeMask);
// we have a char. for our window; see if we are still below TextEdit’s
// limit for the number of characters
if ((key != kDelChar) &&
((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 >= kMaxTELength) )
Failure(eExceedChar,kTEDocErrStrings);
TEKey(key, fDocTE);
UpdateForNewSelection();
AdjustScrollbars(false);
AdjustTE();
fDirty = true;
}
void TEDocument::DoActivate(Boolean becomingActive)
{
if ( becomingActive )
{
RgnHandle tempRgn;
RgnHandle clipRgn;
Rect growRect;
Rect tRect;
// since we don’t want TEActivate to draw a selection in an area where
// we’re going to erase and redraw, we’ll clip out the update region
// before calling it.
tempRgn = NewRgn();
clipRgn = NewRgn();
// save old update region
CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
// put it in local coords
OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
GetClip(clipRgn);
// subtract updateRgn from clipRgn
DiffRgn(clipRgn, tempRgn, tempRgn);
// make it the new clipRgn
SetClip(tempRgn);
TEActivate(fDocTE);
// restore the full-blown clipRgn
SetClip(clipRgn);
// get rid of temp regions
DisposeRgn(tempRgn);
DisposeRgn(clipRgn);
// the controls must be redrawn on activation:
(*fDocVScroll)->contrlVis = kControlVisible;
(*fDocHScroll)->contrlVis = kControlVisible;
// copy rectangles to avoid unsafe object field references!
tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
// the growbox needs to be redrawn on activation:
growRect = fDocWindow->portRect;
// adjust for the scrollbars
growRect.top = growRect.bottom - kScrollbarAdjust;
growRect.left = growRect.right - kScrollbarAdjust;
InvalRect(&growRect);
}
else
{
TEDeactivate(fDocTE);
// the controls must be hidden on deactivation:
HideControl(fDocVScroll);
HideControl(fDocHScroll);
// we draw grow icon immediately, since we deactivate controls
// immediately, and the update delay looks funny
DrawGrowIcon(fDocWindow);
}
}
void TEDocument::DoUpdate()
{
BeginUpdate(fDocWindow); // this sets up the visRgn
if ( ! EmptyRgn(fDocWindow->visRgn) ) // draw if updating needs to be done
{
DrawWindow();
}
EndUpdate(fDocWindow);
}
// calculate how much idle time we need
unsigned long TEDocument::CalcIdle()
{
if (HaveSelection())
return GetCaretTime();
else return kMaxSleepTime; // if we don't have a selection, we don't need to idle
}
// This is called whenever we get a null event et al.
// It takes care of necessary periodic actions. For this program,
// it calls TEIdle.
void TEDocument::DoIdle()
{
TEIdle(fDocTE);
} // DoIdle
// Draw the contents of an application window.
void TEDocument::DrawWindow()
{
Rect tRect;
SetPort(fDocWindow);
tRect = fDocWindow->portRect;
EraseRect(&tRect);
TEUpdate(&tRect, fDocTE);
DrawControls(fDocWindow);
DrawGrowIcon(fDocWindow);
} // DrawWindow
// Return a rectangle that is inset from the portRect by the size of
// the scrollbars and a little extra margin.
void TEDocument::GetTERect(Rect* teRect)
{
*teRect = fDocWindow->portRect;
InsetRect(teRect, kTextMargin, kTextMargin); // adjust for margin
teRect->bottom = teRect->bottom - kScrollbarAdjust; // and for the scrollbars
teRect->right = teRect->right - kScrollbarAdjust;
} // GetTERect
// setup a region which contains the visible text
void TEDocument::GetVisTERgn(RgnHandle rgn)
{
Rect teRect;
teRect = (*fDocTE)->viewRect; // get a local copy of viewRect
SetPort(fDocWindow); // make sure we have right port
LocalToGlobal((Point*) &teRect.top);
LocalToGlobal((Point*) &teRect.bottom);
RectRgn(rgn, &teRect);
// we temporarily change the port’s origin to “globalfy” the visRgn
SetOrigin(-(fDocWindow->portBits.bounds.left),
-(fDocWindow->portBits.bounds.top));
SectRgn(rgn, fDocWindow->visRgn, rgn);
SetOrigin(0, 0);
} // GetTERgn
void TEDocument::ReadFromFile(short refNum)
{
long curPos, size;
Handle h;
// determine how much data is available to read
FailOSErr(GetFPos(refNum,&curPos));
FailOSErr(GetEOF(refNum,&size));
size -= curPos;
// check for size > 32K
if (size > kMaxTELength)
Failure(eExceedChar,kTEDocErrStrings);
// allocate a handle to store it in
h = NewHandle(size);
FailNIL(h);
TRY
{
FailOSErr(FSRead(refNum,&size,*h));
}
RECOVER
{
DisposHandle(h);
}
ENDTRY
// now make the text the current text
SetPort(fDocWindow);
HLock(h);
TESetText(*h,size,fDocTE);
DisposHandle(h);
// make sure everything is up to date
Rect tRect = (*fDocTE)->viewRect;
InvalRect(&tRect);
AdjustTE();
UpdateForNewSelection();
AdjustScrollbars(false);
}
void TEDocument::WriteToFile(short refNum)
{
Handle h;
long size;
// get COPY of TEHandle - doesn't alloc memory
h = (Handle) TEGetText(fDocTE);
size = GetHandleSize(h);
FailOSErr(FSWrite(refNum,&size,*h));
}
// Return boolean value indicating that there is or is not a
// selection in the document
Boolean TEDocument::HaveSelection()
{
if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
return true;
else return false;
}
// Update the TERec's view rect so that it is the greatest multiple of
// the lineHeight that still fits in the old viewRect.
void TEDocument::AdjustViewRect()
{
TEPtr te;
te = *fDocTE;
te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
* te->lineHeight) + te->viewRect.top;
} // AdjustViewRect
// Scroll the TERec around to match up to the potentially updated scrollbar
// values. This is really useful when the window has been resized such that the
// scrollbars became inactive but the TERec was already scrolled.
void TEDocument::AdjustTE()
{
TEPtr te;
te = *fDocTE;
TEScroll((te->viewRect.left - te->destRect.left) - GetCtlValue(fDocHScroll),
(te->viewRect.top - te->destRect.top) -
(GetCtlValue(fDocVScroll) * te->lineHeight),
fDocTE);
} // AdjustTE
// Re-calculate the position and size of the viewRect and the scrollbars.
// kScrollTweek compensates for off-by-one requirements of the scrollbars
// to have borders coincide with the growbox.
void TEDocument::AdjustScrollSizes()
{
Rect teRect;
GetTERect(&teRect);
(*fDocTE)->viewRect = teRect;
AdjustViewRect();
MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
SizeControl(fDocVScroll, kScrollbarWidth,
fDocWindow->portRect.bottom - fDocWindow->portRect.top -
kGrowboxAdjust + kScrollTweek);
MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
SizeControl(fDocHScroll,
fDocWindow->portRect.right - fDocWindow->portRect.left -
kGrowboxAdjust + kScrollTweek,
kScrollbarWidth);
} // AdjustScrollSizes
// Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
// and we don't want that). If the controls are to be resized as well, call the procedure to do that,
// then call the procedure to adjust the maximum and current values. Finally re-enable the controls
// by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
// necessary).
void TEDocument::AdjustScrollbars(Boolean needsResize)
{
// First, turn visibility of scrollbars off so we won’t get unwanted redrawing
(*fDocVScroll)->contrlVis = 0;
(*fDocHScroll)->contrlVis = 0;
if ( needsResize )
AdjustScrollSizes();
AdjustScrollValues(needsResize);
// Now, restore visibility in case we never had to draw during adjustment
(*fDocVScroll)->contrlVis = 0xff;
(*fDocHScroll)->contrlVis = 0xff;
} // AdjustScrollbars
// Calculate the new control maximum value and current value, whether it is the horizontal or
// vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
// vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
// width to the width of the viewRect. The current values are set by comparing the offset between
// the view and destination rects. If necessary, redraw the control by calling ShowControl.
void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
{
short value, lines, max;
short oldValue, oldMax;
TEPtr te;
ControlHandle control;
if (isVert)
control = fDocVScroll;
else control = fDocHScroll;
oldValue = GetCtlValue(control);
oldMax = GetCtlMax(control);
te = *fDocTE; // point to TERec for convenience
if ( isVert )
{
lines = te->nLines;
// since nLines isn’t right if the last character is a return, check for that case
if ( *(*te->hText + te->teLength - 1) == kCrChar )
lines += 1;
max = lines - ((te->viewRect.bottom - te->viewRect.top) /
te->lineHeight);
}
else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
if ( max < 0 )
max = 0;
SetCtlMax(control, max);
// Must deref. after SetCtlMax since, technically, it could draw and therefore move
// memory. This is why we don’t just do it once at the beginning.
te = *fDocTE;
if ( isVert )
value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
else value = te->viewRect.left - te->destRect.left;
if ( value < 0 )
value = 0;
else if ( value > max )
value = max;
SetCtlValue(control, value);
// now redraw the control if asked to or if a setting changed
if ( mustRedraw || (max != oldMax) || (value != oldValue) )
ShowControl(control);
} // AdjustHV
// Simply call the common adjust routine for the vertical and horizontal scrollbars.
void TEDocument::AdjustScrollValues(Boolean mustRedraw)
{
AdjustHV(true, mustRedraw);
AdjustHV(false, mustRedraw);
} // AdjustScrollValues
ClikLoopProcPtr TEDocument::GetClikLoop()
{
return fDocClik;
}
TEHandle TEDocument::GetTEHandle()
{
return fDocTE;
}
void TEDocument::DoCut()
{
long total, contig;
if (ZeroScrap() == noErr)
{
PurgeSpace(&total, &contig);
if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
Failure(eNoSpaceCut,kTEDocErrStrings);
TECut(fDocTE);
fDirty = true;
if (TEToScrap() != noErr)
{
(void) ZeroScrap();
Failure(eNoCut,kTEDocErrStrings);
}
}
UpdateForNewSelection();
AdjustScrollbars(false);
AdjustTE();
}
void TEDocument::DoCopy()
{
if (ZeroScrap() == noErr)
{
TECopy(fDocTE); // after copying, export the TE scrap
if (TEToScrap() != noErr)
{
ZeroScrap();
Failure(eNoCopy,kTEDocErrStrings);
}
}
UpdateForNewSelection();
AdjustScrollbars(false);
AdjustTE();
}
void TEDocument::DoPaste()
{
Handle aHandle;
long oldSize, newSize;
OSErr saveErr;
if ( TEGetScrapLen() + ((*fDocTE)->teLength -
((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
Failure(eExceedPaste,kTEDocErrStrings);
aHandle = (Handle) TEGetText(fDocTE);
oldSize = GetHandleSize(aHandle);
newSize = oldSize + TEGetScrapLen() + kTESlop;
// preflight the growth of the text handle for textedit,
// since it will crash if it doesn't have enough memory
SetHandleSize(aHandle, newSize);
saveErr = MemError();
SetHandleSize(aHandle, oldSize);
if (saveErr != noErr)
Failure(eNoSpacePaste,kTEDocErrStrings);
TEStylPaste(fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
AdjustTE();
}
void TEDocument::DoClear()
{
TEDelete(fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
AdjustTE();
}
void TEDocument::DoSelectAll()
{
long selSize = (*fDocTE)->teLength;
TESetSelect(0,selSize,fDocTE);
UpdateForNewSelection();
}
void TEDocument::SetFontName(Str255 fontName)
{
short fontID;
GetFNum(fontName, &fontID);
SetFont(fontID);
}
void TEDocument::SetFont(short fontID)
{
fTxStyle.tsFont = fontID;
TESetStyle(doFont, &fTxStyle, true, fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
}
void TEDocument::SetFontSize(short fontSize)
{
fTxStyle.tsSize = fontSize;
TESetStyle(doSize, &fTxStyle, true, fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
}
short TEDocument::GetSelectionFontSize()
{
return (fSelSizeContinuous ? fTxStyle.tsSize : -1);
}
short TEDocument::GetSelectionFont()
{
return (fSelFontContinuous ? fTxStyle.tsFont : -1);
}
void TEDocument::SetFaceAttributes(short attributes, Boolean on)
{
if (on)
fTxStyle.tsFace |= attributes;
else
fTxStyle.tsFace &= ~attributes;
TESetStyle(doFace, &fTxStyle, true, fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
}
void TEDocument::SetPlain()
{
fTxStyle.tsFace = normal;
TESetStyle(doFace, &fTxStyle, true, fDocTE);
fDirty = true;
UpdateForNewSelection();
AdjustScrollbars(false);
}
void TEDocument::SetUnderline(Boolean on)
{
SetFaceAttributes(underline, on);
}
void TEDocument::SetOutline(Boolean on)
{
SetFaceAttributes(outline, on);
}
void TEDocument::SetShadow(Boolean on)
{
SetFaceAttributes(shadow, on);
}
void TEDocument::SetItalic(Boolean on)
{
SetFaceAttributes(italic, on);
}
void TEDocument::SetBold(Boolean on)
{
SetFaceAttributes(bold, on);
}
Boolean TEDocument::GetFaceAttributes(short attributes)
{
return (fSelFaceContinuous && (fTxStyle.tsFace & attributes) == attributes);
}
Boolean TEDocument::SelectionIsPlain()
{
return (fSelFaceContinuous && fTxStyle.tsFace == normal);
}
Boolean TEDocument::SelectionIsUnderline()
{
return GetFaceAttributes(underline);
}
Boolean TEDocument::SelectionIsOutline()
{
return GetFaceAttributes(outline);
}
Boolean TEDocument::SelectionIsShadow()
{
return GetFaceAttributes(shadow);
}
Boolean TEDocument::SelectionIsItalic()
{
return GetFaceAttributes(italic);
}
Boolean TEDocument::SelectionIsBold()
{
return GetFaceAttributes(bold);
}
void TEDocument::UpdateForNewSelection()
{
// make sure we catch all the text attributes
short mode = doAll;
fSelFontContinuous = TEContinuousStyle(&mode, &fTxStyle, fDocTE);
// then, ask for each type in which we are interested individually
mode = doFont;
fSelFontContinuous = TEContinuousStyle(&mode, &fTxStyle, fDocTE);
mode = doFace;
fSelFaceContinuous = TEContinuousStyle(&mode, &fTxStyle, fDocTE);
mode = doSize;
fSelSizeContinuous = TEContinuousStyle(&mode, &fTxStyle, fDocTE);
}
/*
Routines used by this class, which don't belong to the class since we use
them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
*/
// Common algorithm for pinning the value of a control. It returns the actual amount
// the value of the control changed.
void CommonAction(ControlHandle control,short* amount)
{
short value, max;
value = GetCtlValue(control);
max = GetCtlMax(control);
*amount = value - *amount;
if ( *amount <= 0 )
*amount = 0;
else if ( *amount >= max )
*amount = max;
SetCtlValue(control, *amount);
*amount = value - *amount;
} // CommonAction
// Determines how much to change the value of the vertical scrollbar by and how
// much to scroll the TE record.
pascal void TEDocument::VActionProc(ControlHandle control,short part)
{
short amount;
WindowPtr window;
TEPtr te;
TEDocument* doc;
if ( part != 0 )
{
window = (*control)->contrlOwner;
doc = (TEDocument*) (TESample::GetTEApplication()->DocList())->FindDoc(window);
te = *(doc->GetTEHandle());
switch ( part )
{
case inUpButton:
case inDownButton: // one line
amount = 1;
break;
case inPageUp: // one page
case inPageDown:
amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
break;
}
if ( (part == inDownButton) || (part == inPageDown) )
amount = -amount; // reverse direction for a downer
CommonAction(control, &amount);
if ( amount != 0 )
TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
}
} // VActionProc
// Determines how much to change the value of the horizontal scrollbar by and how
// much to scroll the TE record.
pascal void TEDocument::HActionProc(ControlHandle control,short part)
{
short amount;
WindowPtr window;
TEPtr te;
TEDocument* doc;
if ( part != 0 )
{
window = (*control)->contrlOwner;
doc = (TEDocument*) (TESample::GetTEApplication()->DocList())->FindDoc(window);
te = *(doc->GetTEHandle());
switch ( part )
{
case inUpButton:
case inDownButton: // a few pixels
amount = 4;
break;
case inPageUp: // a page
case inPageDown:
amount = te->viewRect.right - te->viewRect.left;
break;
}
if ( (part == inDownButton) || (part == inPageDown) )
amount = -amount; // reverse direction
CommonAction(control, &amount);
if ( amount != 0 )
TEScroll(amount, 0, doc->GetTEHandle());
}
} // VActionProc
// Gets called from our assembly language routine, AsmClikLoop, which is in
// turn called by the TEClick toolbox routine. Saves the windows clip region,
// sets it to the portRect, adjusts the scrollbar values to match the TE scroll
// amount, then restores the clip region.
pascal void PascalClikLoop()
{
RgnHandle region;
WindowPtr wind;
TEDocument* doc;
wind = FrontWindow();
doc = (TEDocument*) (TESample::GetTEApplication()->DocList())->FindDoc(wind);
region = NewRgn();
GetClip(region); // save clip
ClipRect(&wind->portRect);
doc->AdjustScrollValues(false);
SetClip(region); // restore clip
DisposeRgn(region);
} // PascalClikLoop
// Gets called from our assembly language routine, AsmClikLoop, which is in
// turn called by the TEClick toolbox routine. It returns the address of the
// default clikLoop routine that was put into the TERec by TEAutoView to
// AsmClikLoop so that it can call it.
pascal ClikLoopProcPtr GetOldClikLoop()
{
TEDocument* doc;
doc = (TEDocument*) (TESample::GetTEApplication()->DocList())->FindDoc(FrontWindow());
if (doc == nil)
return nil;
return doc->GetClikLoop();
} // GetOldClikLoop
DescType TEDocument::GetAppleClass() const
{
return cWindow;
}
long TEDocument::CountContainedObjects(DescType ofType)
{
return MAppleObject::CountContainedObjects(ofType);
}
void TEDocument::DoAppleEvent(const AppleEvent& message, AppleEvent& reply, long refCon)
{
switch (refCon)
{
case cSave:
this->DoAppleSave(message, reply);
break;
case cClose:
this->DoAppleClose(message, reply);
break;
default:
MAppleObject::DoAppleEvent(message, reply, refCon);
break;
}
}
MAppleObject* TEDocument::GetContainedObject(DescType desiredType, DescType keyForm,
const AEDesc& keyData, Boolean& needDisposal)
{
MAppleObject* result = nil;
switch (keyForm)
{
case formPropertyID:
result = this->GetAppleProperty(keyForm, keyData, needDisposal);
break;
}
if (result == nil)
result = MAppleObject::GetContainedObject(desiredType, keyForm, keyData, needDisposal);
return result;
}
MAppleObject* TEDocument::GetAppleProperty(DescType keyForm, const AEDesc& keyData,
Boolean& needDisposal)
{
MAppleObject* result = nil;
// Our property accessors only work on formPropertyID
if (keyForm != formPropertyID)
FailOSErr(errAEWrongDataType);
DescType whichProperty;
if (keyData.descriptorType == typeType)
whichProperty = *(DescType*)*keyData.dataHandle;
else
{
AEDesc whichPropertyDesc;
FailOSErr(AECoerceDesc(&keyData, typeType, &whichPropertyDesc));
whichProperty = *(DescType*)*whichPropertyDesc.dataHandle;
OSErr ignoreErr = AEDisposeDesc(&whichPropertyDesc);
}
switch (whichProperty)
{
case pBounds:
result = new TWindowBounds(this);
FailNIL(result);
needDisposal = true;
break;
case pPosition:
result = new TWindowPosition(this);
FailNIL(result);
needDisposal = true;
break;
case pName:
result = new TWindowName(this);
FailNIL(result);
needDisposal = true;
break;
case pText:
result = new TEditText(this, fDocTE);
FailNIL(result);
needDisposal = true;
break;
}
return result;
}
Boolean TEDocument::CompareAppleObjects(DescType operation, const MAppleObject& toWhat)
{
return MAppleObject::CompareAppleObjects(operation, toWhat);
}
void TEDocument::DoAppleSave(const AppleEvent& message, AppleEvent& /* reply */)
{
DescType returnedType;
Size actualSize;
FSSpec myFSSpec;
OSErr tempErr = AEGetParamPtr(&message, keyAEDestination,
typeFSS, &returnedType, (Ptr) &myFSSpec,
sizeof(myFSSpec), &actualSize);
if (tempErr == noErr)
{
MAppleObject::GotRequiredParameters(message);
this->CloseFile();
fFile = myFSSpec;
this->OpenFile(false, true);
this->DoSave();
}
else if (tempErr != errAEDescNotFound)
FailOSErr(tempErr);
else
MAppleObject::GotRequiredParameters(message);
}
void TEDocument::DoAppleClose(const AppleEvent& message, AppleEvent& /* reply */)
{
DescType saveOpt = kAEYes; // default to save on close
DescType returnedType;
Size actualSize;
OSErr tempErr = AEGetParamPtr(&message, keyAESaveOptions,
cEnumeration, &returnedType,
(Ptr) &saveOpt,
sizeof(saveOpt), &actualSize);
if (tempErr != noErr && tempErr != errAEDescNotFound)
FailOSErr(tempErr);
if (saveOpt == kAEYes)
{
FSSpec myFSSpec;
tempErr = AEGetParamPtr(&message, keyAEDestination, typeFSS,
&returnedType, (Ptr) &myFSSpec, sizeof(myFSSpec),
&actualSize);
if (tempErr == noErr)
{
MAppleObject::GotRequiredParameters(message);
this->CloseFile();
fFile = myFSSpec;
this->OpenFile(false, true);
this->DoSave();
}
else if (tempErr != errAEDescNotFound)
FailOSErr(tempErr);
else
MAppleObject::GotRequiredParameters(message);
}
(void) this->DoClose(false, yesResult, false);
TApplication::GetApplication()->DocList()->RemoveDoc(this);
// We really want ourselves deleted now, but it’s really bad form to delete oneself.
// However, this method gets invoked from an AppleEvent, thus we are a token object
// and can now set our “disposal flag” to true.
TAppleObjectDispatcher::GetDispatcher()->SetTokenObjectDisposal(this, true);
}